/******************************************************************************
 cnome: A C++ source "genes" discover. :-)

 See more details at:

   http://www.codeplex.com/cnome

 The original author site is at: http://www.alemosbarbosa.com/

 Copyright (c) 2009-2010 Alexandre Lemos Barbosa.
 Distributed under the MIT License. (See accompanying file LICENSE-MIT.txt 
 or at http://www.codeplex.com/cnome/license)
 ******************************************************************************/

#include "cnome_config.h"

#include <vector>

#include "parameter.h"
#include "typedefs.h"
#include "symbol.h"

namespace cnome {
using namespace std;
using namespace boost;
using namespace boost::wave;

token_t* prm::get_type() const { return type.get(); }

void prm::set_type(token_t const& t) {
    type = boost::shared_ptr<token_t>(new token_t(t));
}

token_t* prm::get_var() const {
    return var.get();
}

void prm::set_var(token_t const& v) {
    var = boost::shared_ptr<token_t>(new token_t(v));
}

parameter_scope::parameter_scope(): lstprms(), prms(), prmLevel(-1), prmNums() {}
parameter_scope::parameter_scope(const parameter_scope& other): 
    lstprms(other.lstprms), prms(other.prms), prmLevel(other.prmLevel), prmNums(other.prmNums){}

parameter_scope& parameter_scope::operator=(const parameter_scope& r) {
    prms = r.prms;
    lstprms = r.lstprms;
    prmLevel = r.prmLevel;
    prmNums = r.prmNums;
    
    return *this;
}

void parameter_scope::inc_level() {
    ++prmLevel;
    if (prmLevel + 1 > static_cast<int>(prmNums.size()))
        prmNums.push_back(0);
    else
        prmNums[prmLevel] = 0;
}

void parameter_scope::dec_level() {
    prmNums.resize(prmLevel--);
}

void parameter_scope::add_parameter(const token_t& tk) {
    if (static_cast<int>(prmNums.size())  < prmLevel) {
        throw std::exception();   // TODO: Choose a better exception
    }
    else if (static_cast<int>(prmNums.size()) == prmLevel) {
        prmNums.push_back(0);
    }
    ++prmNums[prmLevel];
    prms[get_current_pos()].set_type(tk);
}

void parameter_scope::add_parameter_var(const token_t& tk) {
    prms[get_current_pos()].set_var(tk);
}

parameter_scope::position_type parameter_scope::get_current_pos() const {
    position_type ret(prmNums);
    for (std::size_t i = 0; i < ret.size() && ret[i]; ++i) {
        int pn = --ret[i];
        if (pn < 0) {
            pn = 0;
        }
    }

    return ret;
}

void parameter_scope::clearPrmLevel() {
#if defined(CNOME_DEBUG)
    std::cout << "clearPrmLevel: prmLevel " << prmLevel << std::endl;
    std::cout << "               prms.size() " << prms.size() << std::endl;
#endif
    prmsT::size_type sz;
    int lvl,pn;
    while ((sz=prms.size()) && (lvl=static_cast<int>(prms.rbegin()->first.size()) - 1) >= prmLevel) {
#if defined(CNOME_DEBUG)
        std::cout << "removing from var::prms " << dynamic_cast<parameter_annotation*>(prms.rbegin()->second.get_type()->get_annotation().get())->get_symbol_item_ptr()->to_str()<< std::endl;
#endif
        if (lvl == prmLevel && static_cast<int>(prmNums.size()) > lvl && prmNums[lvl] > 0) {
            pn = --prmNums[lvl];
            if (pn == 0) {
                prmNums.resize(static_cast<int>(prmNums.size()) - 1);
            }
            else if (pn < 0) {
                pn = 0;
            }
        }
        prms.erase(prms.rbegin());
    }
}

void parameter_scope::push_back() {
    lstprms.push_back(lstElement(prms, prmLevel, prmNums));
}

void parameter_scope::pop_back() {
    fromLstElement(lstprms.back());
    lstprms.pop_back();
}

void parameter_scope::clear() {
    prms.clear();
    prmNums.clear();
}

void parameter_scope::clearVars() {
    for(iterator it = prms.begin(); it != prms.end(); ++it) {
        const_cast<prm&>(it->second).clear_var();
    }
}

parameter_scope::level_const_iterator parameter_scope::begin(int level) const {
    return const_cast<parameter_scope*>(this)->prms.begin(pos_level(level));
}

parameter_scope::level_const_iterator parameter_scope::end(int level) const {
    return const_cast<parameter_scope*>(this)->prms.end(pos_level(level));
}

int parameter_scope::get_prmNums(parameter_scope::eLevel level) const {
    return prmNums[prmLevel + static_cast<int>(level)];
}

int parameter_scope::get_level(parameter_scope::const_iterator& it) {
    token_annotation_ptrT annotation = it->second.get_type()->get_annotation();
    if (annotation) {
        if (annotation->get_tag() == PARAMETERANNOTATION)
            return dynamic_cast<parameter_annotation*>(annotation.get())->get_level();
        else if (annotation->get_tag() == PARENTHESISANNOTATION)
            return dynamic_cast<parenthesis_annotation*>(annotation.get())->get_level();
    }

    return -1;
}

int parameter_scope::get_level(iterator& it) {
    const_iterator cIt(it);

    return get_level(cIt);
}

int parameter_scope::get_level(const_reverse_iterator& rit) {
    token_annotation_ptrT annotation = rit->second.get_type()->get_annotation();
    if (annotation) {
        if (annotation->get_tag() == PARAMETERANNOTATION)
            return dynamic_cast<parameter_annotation*>(annotation.get())->get_level();
        else if (annotation->get_tag() == PARENTHESISANNOTATION)
            return dynamic_cast<parenthesis_annotation*>(annotation.get())->get_level();
    }

    return -1;
}

void parameter_scope::cancelLastPrm() {
    if (!prms.size()) {
#if defined(CNOME_DEBUG)
        std::cout << "cancelLastPrm: Canceling no one parameter!!!" << std::endl;
#endif
    }
    else {
#if defined(CNOME_DEBUG)
        std::cout << "cancelLastPrm: Canceling parameter: " << dynamic_cast<parameter_annotation*>(prms.begin()->second.get_type()->get_annotation().get())->get_symbol_item_ptr()->to_str() << std::endl;
#endif
        prms.erase(prms.rbegin());
        int pn = --prmNums[prmLevel];
        if (pn == 0) {
            prmNums.resize(static_cast<int>(prmNums.size()) - 1);
        }
        else if (pn < 0) {
            pn = 0;
        }
    }

#if defined(CNOME_DEBUG)
    std::cout << "cancelLastPrm: Current Parameters:" << endl;
    for (const_iterator it = prms.begin(); it != prms.end(); it++) {
        symbolPtr sybIt(dynamic_cast<parameter_annotation*>(it->second.get_type()->get_annotation().get())->get_symbol_item_ptr());
        std::cout << sybIt->to_str() << " : " 
        << (sybIt->get_type_symbol()?sybIt->get_type_symbol()->to_str():"") << endl;
    }
#endif
}
}  //  cnome
